#include "StdAfx.h"
#include "Race.h"
#include "ActionObject.h"
#include "IMovable.h"
#include "Gate.h"
#include "BoundingVolume.h"
#include "IRaceController.h"
#include "PhysicalObject.h"
#include "AIAgent.h"


///////////////////////////////////////////////////////////////////////////////

Race::Race(IRaceController& raceController,
           const std::vector<ActionObject*>& activePlayers,
           const std::vector<Gate*>& gates,
           const std::vector<AIAgent*>& aiAgents)
      : m_raceController(raceController),
      m_activePlayers(activePlayers),
      m_gates(gates),
      m_aiAgents(aiAgents)
{
}

///////////////////////////////////////////////////////////////////////////////

void Race::update(float timeElapsed)
{
   for (UINT i = 0; i < m_aiAgents.size(); ++i)
   {
      m_aiAgents[i]->update(timeElapsed);
   }

   checkGatesCrossing();
   checkVictoryConditions();
}

///////////////////////////////////////////////////////////////////////////////

void Race::activate()
{
}

///////////////////////////////////////////////////////////////////////////////

void Race::deactivate()
{
}

///////////////////////////////////////////////////////////////////////////////

void Race::checkGatesCrossing()
{
   if (m_gates.size() < 2)
   {
      throw std::logic_error(
         std::string("the game requires at least 2 gates to be defined"));
   }

   for (UINT playerIdx = 0; playerIdx < m_activePlayers.size(); playerIdx++)
   {
      UINT nextGateIdx = m_activePlayers[playerIdx]->getNextGateNo();
      if (nextGateIdx >= m_gates.size())
      {
         throw std::logic_error(
            std::string("Invalid number of the next gate - there aren't so many defined"));
      }

      const AABoundingBox& playerBB = m_activePlayers[playerIdx]->getMovable().getWorldSpaceBoundingBox();
      AABoundingBox gateBB = m_gates[nextGateIdx]->getCrossingArea();
      
      if (playerBB.isCollidingWith(gateBB) == true)
      {
         m_activePlayers[playerIdx]->increaseNumGatesScored();

         // TODO: cos tu nie pasuje...
         UINT nextGateNo = m_activePlayers[playerIdx]->getNextGateNo();
         getNextGateNum(nextGateNo);
         m_activePlayers[playerIdx]->setNextGateNo(nextGateNo);
      }
   }
}

/////////////////////////////////////////////////////////////////////////////

void Race::checkVictoryConditions()
{
   for (UINT playerIdx = 0; playerIdx < m_activePlayers.size(); playerIdx++)
   {
      if (m_activePlayers[playerIdx]->getNumGatesScored() == m_raceController.getNumGatesToScore())
      {
         m_raceController.victory(playerIdx);
      }
   }
}

/////////////////////////////////////////////////////////////////////////////

void Race::getNextGateNum(UINT& gateNum)
{
   gateNum++;
   if (gateNum >= m_gates.size())
   {
      gateNum = 0;
   }
}

/////////////////////////////////////////////////////////////////////////////

void Race::processInput(UCHAR* keyBuffer, float timeElapsed)
{
   triggerMovement(keyBuffer, timeElapsed);
   triggerRotation(keyBuffer, timeElapsed);
   triggerActions(keyBuffer);
}

/////////////////////////////////////////////////////////////////////////////

void Race::triggerMovement(UCHAR* keyBuffer, float timeElapsed)
{
   PhysicalObject* controlledPhysicalObject = m_raceController.getUserControllerPhysicalObject();
   if (controlledPhysicalObject == NULL) return;

   DWORD movementDirection = 0;

   if (IS_PRESSED(keyBuffer, VK_UP))     movementDirection |= DIR_FORWARD;
   if (IS_PRESSED(keyBuffer, VK_DOWN))   movementDirection |= DIR_BACKWARD;

   controlledPhysicalObject->move(movementDirection, timeElapsed);
}

///////////////////////////////////////////////////////////////////////////////

void Race::triggerRotation(UCHAR* keyBuffer, float timeElapsed)
{
   PhysicalObject* controlledPhysicalObject = m_raceController.getUserControllerPhysicalObject();
   if (controlledPhysicalObject == NULL) return;

   DWORD rotationDirection = 0;

   if (IS_PRESSED(keyBuffer, VK_LEFT))   rotationDirection |= DIR_LEFT;
   if (IS_PRESSED(keyBuffer, VK_RIGHT))  rotationDirection |= DIR_RIGHT;
   controlledPhysicalObject->rotate(rotationDirection, timeElapsed);

}

///////////////////////////////////////////////////////////////////////////////

void Race::triggerActions(UCHAR* keyBuffer)
{
   ActionObject* controlledActionObject = m_raceController.getUserControllerActionObject();
   if (controlledActionObject == NULL) return;

   if (IS_PRESSED(keyBuffer, VK_SPACE)) 
   {
      controlledActionObject->shoot();
   }
}

/////////////////////////////////////////////////////////////////////////////